<?php
use Bundle\ModuleBundle\DataSource;
namespace Bundle\ModuleBundle\DataSource;

use Bundle\ModuleBundle\Interfaces\ModuleManagerInterface;
use Bundle\ModuleBundle\Interfaces\ModuleInterface;
use Bundle\ConfigurationBundle\Configuration;

abstract class DataSource
{
    /**
     * @var ModuleManagerInterface
     */
    protected $_moduleManager;

    /**
     * @var ModuleInterface
     */
    protected $_module;

    /**
     * @var Configuration
     */
    protected $_configuration;

    /**
     * @var string
     */
    protected $_entityClassName;

    protected $_fields = array();

    public function __construct(ModuleManagerInterface $mm, ModuleInterface $module, Configuration $configuration)
    {
        $this->_moduleManager = $mm;
        $this->_module = $module;
        $this->_configuration = $configuration;

        $this->preboot();
    }

    /**
     * Создание или загрузка схемы сущности для текущего модуля
     * @abstract
     * @return void
     */
    protected abstract function preboot();

    /**
     * Односторонняя синхронизация сущности с базой данных
     * @abstract
     * @return void
     */
    public abstract function boot();

    /**
     * Добавление поля к сущности для работы в базе данных
     * @param  $name
     * @param  $type
     * @param array $additional
     * @return void
     */
    public function addField($name, $type, $additional = array())
    {
        // Плохо, однако.
        $this->_fields[$name] = $additional;
    }

    public function getEntityClassName()
    {
        return $this->_entityClassName;
    }

    protected function constructEntityClass()
    {
        if($this->_entityClassName == null)
        {
            throw new \Exception('Entity class name is null.');
        }
        
        if(!\class_exists($this->getEntityClassName()))
        {
            $entityGenerator = new \Bundle\ModuleBundle\EntityGenerator($this->_entityClassName,
                $this->_configuration->database['table']);

            foreach($this->_fields as $name => $field)
            {
                $entityGenerator->addField($name, $this->createMappingForField($field, $name), $field->getDefaultValue());
            }

            return $entityGenerator->getCode(true);
        }
    }
    
    /**
     * Метод, в котором получается данные для плагинов модуля
     * @return DataSource\DataSet
     */
    public abstract function fetchData();

    /**
     * Создание меппинга для класса
     */
    public function createMappingForField($field, $name)
    {
        return array(
            'fieldName' => $name, 
            'type' => $field->getType(),
        );
    	
    }
    
    /**
     * Создание сущности на основе значений полей в плагинах
     */
    public function collectEntities()
    {
    	$result = new $this->_entityClassName();
    	    	
        foreach ($this->_fields as $name => $field)
        {
            \call_user_func(array($result, 'set' . $name), $field->hasValue() ? $field->getValue() : $field->getDefaultValue());                           
        }
        
        return $result;
    }

    public function __call($method, $args)
    {
        if(method_exists($this, $method))
        {
        	call_user_func(array($this, $method), $args);
        }
        else
        {
        	throw new \Exception(sprintf('Method (%s) is not defined in datasource (%s).', $method, __CLASS__));
        }
    	return $this;
    }    
}
